/**************************************************************************************
*
*	Copyright (C) 2010 - 2012 Brent Andrew O. Chua - See LICENSE.txt for more info.
*		
*	Filename	- Atomic.h
*
*	Description - 
*
*	Comments	-
*
*	Modification History:
*			Name			   Date					Description
*			MrCodeSushi	-	08/10/2012	-	Creation of this file.
**************************************************************************************/
#ifndef _PSX_ATOMIC_H_
#define _PSX_ATOMIC_H_

#include <intrin.h>
#include "PulseSTD.h"

// Interlock intrinsics declaration

// To declare an interlocked function for use as an intrinsic, First, the function must be declared with the 
// leading underscore. Second, the new function must appear in a #pragma intrinsic statement. For convenience,
// the intrinsic versions of the functions may be #define'd to appear in the source code without the leading 
// underscore.
extern "C" long  __cdecl _InterlockedCompareExchange(long volatile* a_vpDest, long a_Exchange, long a_Comp);
extern "C" long  __cdecl _InterlockedExchange(long volatile* a_vpTarget, long a_Value);
extern "C" long  __cdecl _InterlockedExchangeAdd(long volatile* a_vpAddend, long a_Value);
extern "C" long  __cdecl _InterlockedIncrement(long volatile* a_vpAddend);
extern "C" long  __cdecl _InterlockedDecrement(long volatile* a_vpAddend);
extern "C" __int64 __cdecl _InterlockedCompareExchange64( __int64 volatile * Destination, __int64 Exchange, __int64 Comparand );

// Specifies that calls to functions specified in the pragma's argument list are intrinsic. The compiler 
// generates intrinsic functions as inline code, not as function calls.
#pragma intrinsic (_InterlockedCompareExchange)
#pragma intrinsic (_InterlockedExchange)
#pragma intrinsic (_InterlockedExchangeAdd)
#pragma intrinsic (_InterlockedIncrement)
#pragma intrinsic (_InterlockedDecrement)

namespace Pulse
{
	// NOTE: The return values are the initial value of the destination pointer

	class Atomic
	{
	public:

		static LONG CompareExchange( volatile LONG *pDest, LONG exchange, LONG comparand );

		static INT64 CompareExchange( volatile INT64 *pDestination, INT64 exchange, INT64 comparand );

		static LONG Exchange( volatile LONG *pTarget, LONG value );

		static LONG ExchangeAdd( volatile LONG *pAddend, LONG value );

		static LONG Increment( volatile LONG *pAddend );

		static LONG Decrement( volatile LONG *pAddend );


	};

	PSX_FORCE_INLINE LONG Atomic::CompareExchange( volatile LONG *pDest, LONG exchange, LONG comparand )
	{
		return _InterlockedCompareExchange( pDest, exchange, comparand );
	}

	PSX_FORCE_INLINE INT64 Atomic::CompareExchange( volatile INT64 *pDestination, INT64 exchange, INT64 comparand )
	{
		return _InterlockedCompareExchange64( pDestination, exchange, comparand );
	}

	PSX_FORCE_INLINE LONG Atomic::Exchange( volatile LONG *pTarget, LONG value )
	{
		return _InterlockedExchange( pTarget, value );
	}

	PSX_FORCE_INLINE LONG Atomic::ExchangeAdd( volatile LONG *pAddend, LONG value )
	{
		return _InterlockedExchangeAdd( pAddend, value );
	}

	PSX_FORCE_INLINE LONG Atomic::Increment( volatile LONG *pAddend )
	{
		return _InterlockedIncrement( pAddend );
	}

	PSX_FORCE_INLINE LONG Atomic::Decrement( volatile LONG *pAddend )
	{
		return _InterlockedDecrement( pAddend );
	}
}

#endif /* _PSX_ATOMIC_H_ */

